package cn.ac.sec.oil.data_analysis.protocol;

import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.log4j.Logger;
import cn.ac.sec.oil.data_analysis.calculation.DataCalculation;
import cn.ac.sec.framework.db.SqlUtils;

/**
 * 功能：
 * 处理原始设备测量数据，解析合并多包数据(获取设备号，预测深度，油井压力等，合并波形原始波形数据)
 * 根据预测深度和rate动态调节使用的绘图点数
 * 处理波形数据，计算声速、液位深度
 * 返回数据处理结果，格式(result:数据:x坐标值:y坐标值)
 */
public class ProtocolAnalysis {

	private static final Logger LOG = Logger.getLogger(ProtocolAnalysis.class);
	public DataCalculation dCalculation; //液位深度计算模块
	public StringBuilder measureData = new StringBuilder(); //根据若干包组成完整的一次测量数据
	private volatile boolean isLastPacket = false; //指示将要处理包是否为最后一包数据
	public float frequency = 5f; //采样频率，单位ms
	public String devId; //设备编号
	public String devId16; //设备编号,16进制
	public String totalPacketNum; //总包数
	public int currTotalPacketNum = 0; //当前收到的总包数
	public String measureDeep = "2000"; //经数据处理计算得到的油井深度
	public String forecastDeep; //设备上报预测深度
	public String pValue; //油井压力
	public String Longitude; //设备上报经度
	public String latitude; //设备上报纬度
	public String wifiSsid; //WiFi名称
	public String wifiPasswd; //WiFi密码
	public String reportTime; //设备数据上报时间
	public float rate = 3.5f; //根据预测深度乘rate计算实际使用的点数
	public int actualNum; //实际使用的点数
	public String originData; //用于将完整的测量数据返回至调用函数，去绘图
	public String xStr; //x坐标数据
	public String yStr; //y坐标数据
	public String devAddr; //\127.0.0.1:13380
	
	/**
	 * 功能：构造函数，创建类实例
	 */
	public ProtocolAnalysis() {
		dCalculation = new DataCalculation();	
	}

	/**
	 * 功能：接收包数据并进行处理,控制总体流程
	 * 根据返回字符串的头进行判断
	 * 返回wait:no需要继续接受数据包，返回result：resultStr:xAxis:yAxis,表示一次测量多包数据处理结果及包数据去绘图用
	 */
	public String dataAnalysis(String data) {
		
		String[] devData = data.split("[;]");
		devAddr = devData[0].substring(1); //取第一个之后的字符串构成的新字符串
		dataPacketAnalysis(devData[1]); 
		
		/*判定包是否收齐*/
		if (isLastPacket) { //接收齐
			
			if (Integer.parseInt(totalPacketNum) == currTotalPacketNum) {
				currTotalPacketNum = 0;
				LOG.info("本次测量共" + totalPacketNum + "包数据，此时全部收齐，进行数据分析处理 ...");
				/*规范波形数据格式*/
				originData = measureData.toString();
				originData = originData.substring(0, originData.length()-1); //去除最后的","
				int dataLength = originData.split("[,]").length;
				
				/*构建绘图的x、y轴数据*/
				xStr = xAxisValue(frequency, 340f, dataLength, 2); //限制点数，xStr = xAxisValue(5f, 340f, dataLength);则不限
				yStr = yAxisValue(originData); //限制点数，yStr = originData;则不限
				
				/*调用计算模块，根据包数据获取结果:使用标准差分析液位值*/
				measureDeep = xStr.split("[,]")[dCalculation.dataAnalysis(strToFloatArray(yStr), 45) - 2]; 
				LOG.info("测得液位深度： " + measureDeep + "m");
				LOG.info("本组数据处理结果 [液位处对应标准差:" + dCalculation.max_SDevition + "; 振幅：" + dCalculation.levelPoint + "]");
				
				/*释放或初始化全局变量*/
				measureData.delete(0, measureData.length());
				
				writeSql(); //将一次测量数据及数据处理结果写入数据库dev_data和oilwell_data
				return "result:" + returnResult(); //限制点数
			}
			
			isLastPacket = false;
		}
		
		return "wait:no";	
	}
	
	/**
	 * 功能：解析包数据，函数每执行一次处理一包数据
	 * 格式：0xaa n1 n2 n3......n10  n11...n1019
	 * 返回true：处理成功，false：数据无效或处理失败
	 * 示例数据：0xaa0x50x1
	 */
	public boolean dataPacketAnalysis(String originData) {
		
		String currPacketNo;
		String currPacketValue;
		
		if (!originData.startsWith("aa")) {
			LOG.info("不是有效的数据包");
			return false;
		}
		
		LOG.info("数据有效，开始处理 ...");
		if (isLastPacket) {
			
		}
		currTotalPacketNum ++;
		
		/*拆分data*/
		String[] dataInfo = originData.split(" "); //按空格split
		LOG.info("数据包长度为： " + dataInfo.length);
		//arrayErgodic(dataInfo);
		
		/*获取总包数量：n1*/
		totalPacketNum =  Integer.toString(Integer.parseInt(dataInfo[1],16));
		LOG.info("总包数量：" + totalPacketNum);
		
		/*获取当前包编号n2,在此认为包是按照发送顺序接收到的*/
		//currPacketNo = dataInfo[2];
		currPacketNo = Integer.toString(Integer.parseInt(dataInfo[2],16));
		LOG.info("当前包编号：" + currPacketNo);
		if (currPacketNo.equals(totalPacketNum)) {
			isLastPacket = true;
		}
		
		/*获取设备编号或其他检测信息:n3-n19*/
		if (currPacketNo.equals("1")) { //设备编号(只是前几包有数据，只解析前几包的n3-n19即可)
			
			devId = asciiToStr(arrayToStrBase(dataInfo, 2, 18, ",")); //n3-n19
			devId16 = asciiStrToStr(arrayToStrBase(dataInfo, 2, 18, ","));
			LOG.info("设备编号为： " + devId);
		} else if (currPacketNo.equals("2")) { //压力等检测结果
			
			/*油井是否有压*/
			if (dataInfo[3].equals("00")) {
				LOG.info("无压井");
			} else {
				String pressureVale = Integer.toString(Integer.parseInt(dataInfo[3],16)); 
				pValue = pressureVale.substring(0, pressureVale.length()-1) + "." + pressureVale.substring(pressureVale.length()-1);
				LOG.info("有压井，压力为： " + pValue + "MPa");
			}
			
			/*设备上报采集时间*/
			reportTime = Integer.toString(Integer.parseInt(dataInfo[4],16)) + ":" + Integer.toString(Integer.parseInt(dataInfo[5],16));
			LOG.info("设备上报测量时间： " + reportTime);
			
			/*wifi信息*/
			String ssid = arrayToStrBase(dataInfo, 5, 10, ",");
			String passwd = arrayToStrBase(dataInfo, 11, 18, ",");		
			wifiSsid = asciiStrToStr(ssid.substring(0, ssid.length() - 1));
			wifiPasswd = asciiStrToStr(passwd.substring(0, passwd.length() - 1));
			LOG.info("wifiSsid:" + wifiSsid + ", wifiPasswd:" + wifiPasswd);
			
		} else if (currPacketNo.equals("3")) { //设备GPS信息(N:北纬，纬度)
			
			latitude = arrayToStr(dataInfo, 2, 18, "");
			LOG.info("纬度： " + latitude);
		} else if (currPacketNo.equals("4")) { //设备GPS信息(E：东经，经度)
			
			Longitude = arrayToStr(dataInfo, 2, 18, "");
			LOG.info("经度： " + Longitude);
		} else if (currPacketNo.equals("5")) { //设备GPS信息
			
			forecastDeep = Integer.toString(Integer.parseInt(dataInfo[3],16));
			LOG.info("预测深度(1-5KM): " + forecastDeep + "KM");
		} else {
			LOG.info("备用");
		}
		
		/*for (int i = 0; i < Integer.parseInt(totalPacketNum); i++) { //对于每包数据
			//暂时不用每包都解析
		}*/
		
		/*获取数据ad:n20-n1009,共计500*/
		currPacketValue = arrayToStr(dataInfo, 19, 1020, ",");
		currPacketValue = strHandle(currPacketValue);
		
		measureData.append(currPacketValue);
		//LOG.info("第" + currPacketNo + "包数据为: " + currPacketValue);
		LOG.info("当前接收到总包数： " + currTotalPacketNum);
		
		return true;
	}
	
	/**
	 * 功能：数据返回格式，根据一次测量的多包数据的处理结果，指定处理结果的返回格式
	 * 数据返回总长52:0xbb n1 n2 n3 n4 n5 n6 n7 …n50 0xcc；
	 */
	public String returnResult() {
		
		String str = "0x01 0x00 " + devId16 + " " + addrResult() + " 0x" + hexConvert16(forecastDeep) + measureDeepResult()
				+ " " + wifiSsid + " " + wifiPasswd;
		
		str = "0xbb " + str + " 0x00 0x00 0x00 " + mSum(str) + " 0xcc";
		LOG.info("向客户端返回： " + str);
		return str;
	}
	
	
	/**
	 * 功能： 遍历数组
	 */
	public void arrayErgodic(String[] array) {
		
		System.out.println("遍历数组：");
		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i] + ";");
		}
		System.out.println();
	}
	
	/**
	 * 功能： 数组连续指定位拼接字符串
	 * 从start开始，至(包含)end结束,同时可以指定分隔符
	 * 十六进制转十进制：Integer.parseInt("8C",16)
	 * 十进制转十六进制：Integer.toHexString(200)
	 */
	public String arrayToStr(String[] array, int start, int end, String separator) {
		
		StringBuilder devNo = new StringBuilder();
		for (int i = 0; i < array.length; i++) {

			if (i > end + 1) {
				return devNo.toString();
			}
			if (i > start) {
				//devNo.append(array[i]).append(separator); //十六进制
				devNo.append(Integer.parseInt(array[i], 16)).append(separator); //包含十六进制转十进制
			}
		}
		
		return devNo.toString();
	}
	
	/**
	 * 功能： 数组连续指定位拼接字符串
	 * 从start开始，至(包含)end结束,同时可以指定分隔符
	 */
	public String arrayToStrBase(String[] array, int start, int end, String separator) {
		
		StringBuilder devNo = new StringBuilder();
		for (int i = 0; i < array.length; i++) {

			if (i > end + 1) {
				return devNo.toString();
			}
			if (i > start) {
				devNo.append(array[i]).append(separator); 
			}
		}
		
		return devNo.toString();
	}
	
	/**
	 * 功能：将string转换为float[]
	 * str:measureData.toString()
	 */
	public float[] strToFloatArray(String str) {
		
		String[] strArray = str.split("[,]");
		float[] fArray = new float[strArray.length];
		
		for (int i = 0; i < strArray.length; i++) {
			fArray [i] = Float.parseFloat(strArray[i]);
		}
		return fArray;
	}
	
	/**
	 * 功能：获取当前时间
	 * 在收到第一包的时候获取当前时间，作为本次测量的时间
	 * 简单处理：选择将合并测量结果时的时间作为本次的测量时间
	 * yyyy-MM-dd HH:mm:ss或"HH:mm"均可以
	 */
	public String getCurrentTime() {
		
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //设置日期格式
		return simpleDateFormat.format(new Date()); //new Date()为获取当前系统时间
	}
	
	/**
	 * 功能：十进制转十六进制
	 * 字符串-int-16进制
	 */
	public String hexConvert16(String str) {
		
		return Integer.toHexString(Integer.parseInt(str));
	}
	
	/**
	 * 功能：将一个字符串中相邻的两个值合为一个值
	 * 1,2,3,4,5,6,7,8,9,0 -> 12,34,56,78,90,
	 */
	public String strHandle(String str) {
		
		StringBuilder packetData = new StringBuilder();
		String[] strArray = str.split("[,]"); //strArray.length为1000
		for (int i = 0; i < strArray.length; i++) {
			
			if (i%2 == 0) { //i为偶数
				packetData.append(strArray[i] + strArray[i+1]).append(",");
			}
		}
		
		return packetData.toString();
	}	
	
	/**
	 * 功能：根据采样频率和声波速度生成波形图的横坐标
	 * frequency:采样频率
	 * speed:声速
	 * length:数据点数(即由y值数量确定x值数量)
	 */
	public String xAxisValue(Float frequency, Float speed, int length) {
		
		StringBuilder xAxis = new StringBuilder(); //存放最终生成的x坐标
		float baseValue = speed*frequency/1000;
		LOG.info("x轴坐标基础值： " + baseValue);
		LOG.info("数据点数为： " + length);
		
		for (int i = 0; i < length; i++) {
			
			if (i==0) { 
				xAxis.append(decimalFromat(baseValue * (i+1))); //控制小数位数，同时使最后一个数不加","
			} else {
				xAxis.append("," + decimalFromat(baseValue * (i+1)));
			}	
		}
		
		return xAxis.toString();
	}
	
	/**
	 * 功能：根据采样频率和声波速度生成波形图的横坐标：重载
	 * forecastDeep:设备上报预测深度，由此确定绘图使用的点数，避免过多
	 */
	public String xAxisValue(Float frequency, Float speed, int length, int forecastDeep) {
		
		StringBuilder xAxis = new StringBuilder(); //存放最终生成的x坐标
		float baseValue = speed*frequency/1000;
		LOG.info("x轴坐标基础值： " + baseValue);
		
		for (int i = 0; i < length; i++) {
			
			if (baseValue * (i+1) > forecastDeep * rate * 1000) {
				actualNum = i;
				LOG.info("数据点数为： " + actualNum);
				return xAxis.toString();
			}
			
			if (i==0) { 
				xAxis.append(decimalFromat(baseValue * (i+1))); //控制小数位数，同时使最后一个数不加","
			} else {
				xAxis.append("," + decimalFromat(baseValue * (i+1)));
			}	
		}
		return xAxis.toString();
	}
	
	/**
	 * 功能：根据actualNum处理originData的数据长度
	 */
	public String yAxisValue(String originData) {
		
		StringBuilder yAxis = new StringBuilder();
		String[] yArray = originData.split("[,]");
		
		for (int i = 0; i < actualNum + 1; i++) {
			if (i == 0) {
				yAxis.append(yArray[i]);
			} else {
				yAxis.append("," + yArray[i]);
			}
		}
		return yAxis.toString();
	}
	
	/**
	 * 功能：使float类型保留一位小数
	 */
	public String decimalFromat(float num) {
		
		DecimalFormat fnum = new DecimalFormat("##0.0"); 
		String fStr =fnum.format(num); 
		return fStr;
	}
	
	/**
	 * 功能：将入单条数据写入数据库
	 */
	public void writeSql(String table, String field, String value) {
		
		String sqlStr = "INSERT INTO " + table + " (" + field + ") values " + "('" + value + "')"; 
		
		try {
			SqlUtils.update(sqlStr); //将数据写入数据库
		} catch (SQLException e) {
			e.printStackTrace();
		}			
	}
	
	/**
	 * 功能：将数据处理结果写入数据库
	 */
	public void writeSql() {
		
		String devDataSql = "INSERT INTO dev_data (measure_time, dev_id, frequency, set_pressure, gps_n, gps_e, forecast_deep, measure_deep, wifi_ssid, " +
				"wifi_passwd, origin_data, xAxis, yAxis) values ('" + getCurrentTime() + "','" + devId + "','" + frequency + "','" + pValue + 
				"','" + latitude + "','" + Longitude + "','" + forecastDeep + "','" +  measureDeep + "','" + wifiSsid + "','" + 
				wifiPasswd + "','" + originData + "','" + xStr + "','" + yStr + "')";
		String oilwellDataSql = "INSERT INTO oilwell_data (measure_time, dev_id, liquid_level, set_pressure) values ('" + getCurrentTime() + "','" 
				+ devId + "','" +  measureDeep + "','" +  pValue + "')";
		
		try {
			SqlUtils.update(devDataSql); //将数据写入数据库
			SqlUtils.update(oilwellDataSql); 
		} catch (SQLException e) {
			e.printStackTrace();
		}			
	}
	
	/**
	 * 功能：ascii字符串转字符串
	 */
	public static String asciiToStr(String asciiStr) {

		StringBuffer sBuffer = new StringBuffer();
		String[] chars = asciiStr.split(",");
		for (int i = 0; i < chars.length; i++) {
			//sBuffer.append((char) Integer.parseInt(chars[i]));
			sBuffer.append(hexTolLetter(chars[i]));
		}
		return sBuffer.toString();
	}
	
	/**
	 * 功能：ascii字符串转字符串
	 * 暂时没有用到**********************
	 */
	public String asciiToStrSpectator(String asciiStr) {

		StringBuffer sBuffer = new StringBuffer();
		String[] chars = asciiStr.split(","); 
		for (int i = 0; i < chars.length; i++) {
			if (i < chars.length - 1) {
				sBuffer.append((char) Integer.parseInt(chars[i])).append(",");
			} else {
				sBuffer.append((char) Integer.parseInt(chars[i]));
			}
		}
		return sBuffer.toString();
	}
	
	/**
	 * 功能：将ascii字符串转换为16进制的字符串
	 */
	public String asciiStrToStr(String str) {
		
		String[] temp = str.split("[,]");
		StringBuffer sBuffer = new StringBuffer();
		for (int i = 0; i < temp.length; i++) {
			if (i < temp.length - 1) {
				//sBuffer.append("0x").append(Integer.toHexString(Integer.parseInt(temp[i]))).append(" ");
				sBuffer.append("0x").append(hexTolLetter(temp[i])).append(" ");
			} else {
				//sBuffer.append("0x").append(Integer.toHexString(Integer.parseInt(temp[i])));
				sBuffer.append("0x").append(hexTolLetter(temp[i]));
			}
		}
		return sBuffer.toString();
	}
	
	/**
	 * 功能：返回IP和port处理结果
	 */
	public String addrResult() {
		
		StringBuilder stringBuilder = new StringBuilder();
		String[] ipArray = devAddr.split("[:]")[0].split("[.]");
		int port = Integer.parseInt(devAddr.split("[:]")[1]);
		
		for (int i = 0; i < ipArray.length; i++) {
			if (i < ipArray.length - 1) {
				stringBuilder.append("0x").append(Integer.toHexString(Integer.parseInt(ipArray[i]))).append(" 0x2E ");
			} else {
				stringBuilder.append("0x").append(Integer.toHexString(Integer.parseInt(ipArray[i]))).append(" 0x3A ");
			}
		}

		stringBuilder.append("0x").append(Integer.toHexString(port/256)).append(" 0x").append(Integer.toHexString(port%256));		
		return stringBuilder.toString();
	}
	
	/**
	 * 功能：返回measureDeep处理结果
	 */
	public String measureDeepResult() {
		
		measureDeep = measureDeep.split("[.]")[0];
		return " 0x" + Integer.toHexString(Integer.parseInt(measureDeep)/256) + " 0x" + Integer.toHexString(Integer.parseInt(measureDeep)%256);
	}
	
	/**
	 * 功能：求若干个十六进制数的
	 * 先将先转为十进制求和在转为十六进制
	 * 输入(0x12 0x22 ...) --> 输出(0xnn)
	 */
	public static String mSum(String str) {
		
		String[] array = str.split(" ");
		int[] iArray = new int[array.length];
		int sum = 0;
		
		for (int i = 0; i < array.length; i++) {
			array[i] = array[i].substring(2, array[i].length());
			iArray[i] = Integer.parseInt(array[i], 16);
		}
		
		for (int i = 0; i < iArray.length; i++) {
			sum += iArray[i];
		}
		
		return "0x" + Integer.toHexString(sum%256);
	}
	
	/** 
	 * 功能：根据16进制数字返回对应的ascii值，其中30对应0
	 * @param hex  要转化的16进制数，用逗号隔开 如：53,68,61,64,6f,77 
	 */  
	public static String hexTolLetter(String hex) {  
		
	    StringBuilder sb = new StringBuilder();  
	    String[] split = hex.split(",");  
	    for (String str : split) {  
	        int i = Integer.parseInt(str, 16);  
	        sb.append((char)i);  
	    }  
	    return sb.toString();  
	}
}
